package vm


import ."api"


// `SELF`指令(`iABC`模式)把对象和方法拷贝到相邻的两个目标寄存器中
// 对象在寄存器中，索引由操作数B指定
// 方法名在常量表里，索引由操作数C指定.目标寄存器由操作数A指定
// R(A+1) := R(B); R(A) := R(B)[RK(C)]
func self(i Instruction, vm LuaVM) {

	a, b, c := i.ABC()
	a += 1
	b += 1

	vm.Copy(b, a+1)
	vm.GetRK(c)
	vm.GetTable(b) // TODO???
	vm.Replace(a)
}



// `CLOSURE`指令(`iABx`模式)把当前`Lua`函数的子函数原型实例化为闭包
// 放入由操作数A指定的寄存器中
// 子函数原型来自于当前函数原型的子函数原型表，索引由操作数`Bx`指定
// R(A) := closure(KPROTO[Bx])
func closure(i Instruction, vm LuaVM) {

	a, bx := i.ABx()
	a += 1

	vm.LoadProto(bx)	// 把当前函数的子函数原型(索引由参数指定)实例化为闭包并推入栈顶
	vm.Replace(a)
}


// `VARARG`指令(`iABC`模式)把传递给当前函数的变长参数加载到连续多个寄存器中
// 其中第一个寄存器的索引由操作数A指定，寄存器数量由操作数B指定，操作数C没有用
// R(A), R(A+1), ... , R(A+B-2) = vararg
func vararg(i Instruction, vm LuaVM) {

	a, b, _ := i.ABC()
	a += 1

	if b != 1 {
		// 操作数B若大于1, 表示把B-1个vararg参数复制到寄存器
		// 否则只能等于0, 表示把全部vararg参数复制到寄存器
		vm.LoadVararg(b - 1)
		_popResults(a, b, vm)
	}
}


// `TFORCALL`指令(`iABC`模式)
// R(A+3), ..., R(A+2+C) := R(A)(R(A+1), R(A+2))
func tForCall(i Instruction, vm LuaVM) {

	a, _, c := i.ABC()
	a += 1

	_pushFuncAndArgs(a, 3, vm)
	vm.Call(2, c)
	_popResults(a+3, c+1, vm)
}


// TAILCALL指令的临时实现方法，没有做任何优化
// return R(A)(R(A+1), ... , R(A+B-1))
func tailCall(i Instruction, vm LuaVM) {

	a, b, _ := i.ABC()
	a += 1
	c := 0

	nArgs := _pushFuncAndArgs(a, b, vm)
	vm.Call(nArgs, c-1)
	_popResults(a, c, vm)
}



// `CALL`指令(`iABC`模式)调用`Lua`函数
// 其中被调用函数位于寄存器中，索引由操作数A指定
// 需要传递给被调函数的参数值也在寄存器中，紧挨着被调函数，数量由操作数B指定
// 函数调用结束后，原先存放函数和参数值的寄存器会被返回值占据，具体有多少个返回值则由操作数C指定
// R(A), ... , R(A+C-2) = R(A)(R(A+1), ... , R(A+B-1))
func call(i Instruction, vm LuaVM) {

	a, b, c := i.ABC()
	a += 1

	nArgs := _pushFuncAndArgs(a, b, vm)
	vm.Call(nArgs, c-1)
	_popResults(a, c, vm)
}


// 把被调函数和参数值推入栈顶
func _pushFuncAndArgs(a, b int, vm LuaVM) (nArgs int) {

	// 如果操作数B大于0, 需要传递的参数是B-1个
	// 循环调用PushValue方法把函数和参数值推入栈顶即可
	if b >= 1 {

		// 由于我们给指令预留的栈顶空间是很少的，而传入参数数量却不确定
		// 这里需要调用CheckStack()方法确保栈顶有足够的控件可以容纳函数和参数值
		vm.CheckStack(b)
		for i := a; i < a + b; i++ {
			vm.PushValue(i)
		}

		return b - 1
	} else {
		_fixStack(a, vm)
		return vm.GetTop() - vm.RegisterCount() - 1
	}
}


func _fixStack(a int, vm LuaVM) {

	x := int(vm.ToInteger(-1))
	vm.Pop(1)

	vm.CheckStack(x - a)
	for i := a; i < x; i++ {

		vm.PushValue(i)
	}
	vm.Rotate(vm.RegisterCount() + 1, x - a)
}


// 把函数调用结果弹出栈顶
func _popResults(a, c int, vm LuaVM) {

	if c == 1 {  // no results

	} else if c > 1 {  // c-1 results

		for i := a+c-2; i >= a; i-- {
			vm.Replace(i)
		}
	} else {  // C == 0, 那么需要把被调函数的返回值全部返回.
		// 对于最后这种情况, 干脆就把这些返回值先留在栈顶, 反正后面也是要把他们在推入栈顶的
		// 我们往栈顶推入一个整数值, 标记这些返回值原本是要移动到哪些寄存器中
		vm.CheckStack(1)
		vm.PushInteger(int64(a))
	}
}

// `RETURN`指令(`iABC`模式)把存放在连续多个寄存器里的值返回给主调函数
// 其中第一个寄存器的索引由操作数A指定，寄存器数量由操作数B指定，操作数C没用
// return R(A), ... , R(A+B-2)
func _return(i Instruction, vm LuaVM) {

	a, b, _ := i.ABC()

	a += 1

	if b == 1 {  // no return valus
	} else if b > 1 {  // b-1 return values
		vm.CheckStack(b - 1)
		for i := a; i <= a + b - 2; i++ {
			vm.PushValue(i)
		}
	} else {
		// 一部分返回值已经在栈顶了，调用_fixStack()函数把另一部分也推入栈顶
		_fixStack(a, vm)
	}
}